﻿#region License
// MIT License
//
// Copyright(c) 2019-2025 ZhangYu
// https://gitee.com/zhangyu800/litebyte
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#endregion
#region Intro
// Purpose: Syntax analyze
// Author: ZhangYu
// CreateDate: 2019-10-18
// LastModifiedDate: 2025-02-27
#endregion
namespace LiteByte.Analyzers {

    using System;
    using System.Text;
    using System.Collections.Generic;
    using LiteByte.Common;

    /// <summary> 语法分析器 | Syntax analyzer </summary>
    public class SyntaxAnalyzer {

        #region Fields
        private int index;
        private List<LexicalToken> tokens = null;
        private StringBuilder error = null;
        #endregion

        #region API
        /// <summary> 语法解析 | Syntax parse  </summary>
        public SyntaxResult Analyze(List<LexicalToken> tokens) {
            if (tokens == null || tokens.Count == 0) throw new ArgumentNullException("tokens", "Tokens can not be null or empty");
            // 初始化数据
            index = 0;
            this.tokens = tokens;
            error = new StringBuilder();
            List<TypeData> types = new List<TypeData>();
            HashSet<string> names = new HashSet<string>();

            // 分析所有token
            while (index < tokens.Count) {
                TypeData type = ParseClass();
                // 检查是否出错
                if (error.Length != 0 || type == null) break;
                // 检查名称是否重复
                if (!names.Contains(type.Name)) {
                    types.Add(type);
                    names.Add(type.Name);
                } else {
                    AddError($"Class name: {type.Name} is already exists!");
                    break;
                }
                index++;
            }

            // 返回分析报告
            SyntaxResult report = new SyntaxResult(error.Length == 0, error.ToString(), types);
            ClearData();
            return report;
        }
        #endregion

        #region Token
        private static readonly LexicalToken EmptyToken = new LexicalToken();

        private LexicalToken GetToken(int index) {
            return index >= 0 && index < tokens.Count ? tokens[index] : EmptyToken;
        }
        #endregion

        #region ParseType
        private bool IsGeneric(int index) {
            return GetToken(index + 1).tag == TokenTag.LAngle;
        }

        private bool IsArray(int index) {
            return GetToken(index + 1).tag == TokenTag.LSquare;
        }

        private bool IsArrayLRSquare(int first, int second) {
            return GetToken(first).tag == TokenTag.LSquare && GetToken(second).tag == TokenTag.RSquare;
        }

        private enum ClassPart {
            ClassType, ClassName, LBrace, FieldType, FieldName, Semicolon, RBrace
        }

        /// <summary> 解析自定义类 </summary>
        private TypeData ParseClass() {
            // 初始化数据
            string classType = "";
            string className = "";
            TypeData fieldType = null;
            string fieldName = null;
            List<FieldData> fieldInfos = new List<FieldData>();
            HashSet<string> fieldNames = new HashSet<string>();
            ClassPart part = ClassPart.ClassType;
            // 遍历Token
            while (index < tokens.Count && error.Length == 0) {
                LexicalToken token = tokens[index];
                TokenTag tag = token.tag;
                string value = token.value;
                // 解析字段 查看中间的Token是否符合 string name; 这种格式
                switch (part) {
                    case ClassPart.ClassType:
                        if (token.tag == TokenTag.AccessModifier) {
                            // 忽略访问修饰符
                        } else if (value == "class" || value == "struct") {
                            // 第一个Token必须为class或struct
                            classType = value;
                            part = ClassPart.ClassName;
                        } else {
                            AddError(ValueError(token, "class\" or \"struct"));
                        }
                        break;
                    case ClassPart.ClassName:
                        // 查找类名
                        if (tag == TokenTag.Identifier) {
                            className = value;
                            part = ClassPart.LBrace;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case ClassPart.LBrace:
                        // 查找结构体开始符 {
                        if (tag == TokenTag.LBrace) {
                            part = ClassPart.FieldType;
                        } else {
                            AddError(ValueError(token, "{"));
                        }
                        break;
                    case ClassPart.FieldType:
                        // 字段类型
                        if (tag == TokenTag.RBrace) {
                            // 结束 (没有任何错误时 返回类型)
                            if (error.Length == 0) return new TypeData(className, fieldInfos, classType == "struct");
                            return null;
                        } else if (tag == TokenTag.AccessModifier) {
                            // 忽略访问修饰符
                        } else if (tag == TokenTag.Identifier) {
                            fieldType = ParseType();
                            part = ClassPart.FieldName;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case ClassPart.FieldName:
                        // 字段名称
                        if (tag == TokenTag.Identifier) {
                            fieldName = value;
                            part = ClassPart.Semicolon;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case ClassPart.Semicolon:
                        // 分号
                        if (tag == TokenTag.Semicolon) {
                            // 检查字段名称是否重复
                            if (!fieldNames.Contains(fieldName)) {
                                // 添加字段数据
                                fieldInfos.Add(new FieldData(fieldType, fieldName));
                                fieldNames.Add(fieldName);
                                part = ClassPart.FieldType;
                            } else {
                                AddError(FieldNameExistsError(token, fieldName));
                            }
                        } else {
                            AddError(ValueError(token, ";"));
                        }
                        break;
                }
                index++;
            }
            // 添加未完成的解析错误
            if (error.Length == 0) AddClassPartError(part);
            return null;
        }

        /// <summary> 解析类型 </summary>
        private TypeData ParseType() {
            if (IsGeneric(index)) return ParseGeneric();
            if (IsArray(index)) return ParseArray();
            // IsBaseType
            string value = GetToken(index).value;
            TypeData baseType = CommonUtil.GetBaseType(value);
            if (baseType != null) return baseType;
            return new TypeData(value, TypeName.Object);
        }

        private enum GenericPart {
            BaseType, LAngle, GenericType, Comma, RAngle
        }

        /// <summary> 解析泛型 </summary>
        private TypeData ParseGeneric() {
            string baseType = null;
            List<TypeData> genericTypes = new List<TypeData>();
            GenericPart part = GenericPart.BaseType;
            while (index < tokens.Count && error.Length == 0) {
                LexicalToken token = GetToken(index);
                switch (part) {
                    case GenericPart.BaseType:
                        if (token.tag == TokenTag.Identifier) {
                            baseType = token.value;
                            part = GenericPart.LAngle;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case GenericPart.LAngle:
                        if (token.tag == TokenTag.LAngle) {
                            part = GenericPart.GenericType;
                        } else {
                            AddError(ValueError(token, "<"));
                        }
                        break;
                    case GenericPart.GenericType:
                        if (token.tag == TokenTag.Identifier) {
                            genericTypes.Add(ParseType());
                            part = GenericPart.Comma;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case GenericPart.Comma:
                        if (token.tag == TokenTag.Comma) {
                            part = GenericPart.GenericType;
                        } else if (token.tag == TokenTag.RAngle) {
                            part = GenericPart.RAngle;
                            index--;
                        } else {
                            AddError(ValueError(token, ",\" or \">"));
                        }
                        break;
                    case GenericPart.RAngle:
                        if (token.tag == TokenTag.RAngle) {
                            bool isList = CommonUtil.IsList(baseType, genericTypes.Count);
                            bool isHashMap = CommonUtil.IsHashMap(baseType, genericTypes.Count);
                            string name = CreateGenericName(baseType, genericTypes);
                            TypeData type = new TypeData(name, genericTypes, isList, isHashMap);
                            if (IsArrayLRSquare(index + 1, index + 2)) {
                                // Array
                                index += 2;
                                return new TypeData(name + "[]", type);
                            } else {
                                // GenericType
                                return type;
                            }
                        } else {
                            AddError(ValueError(token, ">"));
                        }
                        break;
                }
                index++;
            }
            // 添加未完成的解析错误
            if (error.Length == 0) AddGenericPartError(part);
            return null;
        }

        private enum ArrayPart {
            Type, LSquare, RSquare
        }

        /// <summary> 解析数组 </summary>
        private TypeData ParseArray() {
            string baseName = null;
            ArrayPart part = ArrayPart.Type;
            while (index < tokens.Count && error.Length == 0) {
                LexicalToken token = GetToken(index);
                switch (part) {
                    case ArrayPart.Type:
                        if (token.tag == TokenTag.Identifier) {
                            baseName = token.value;
                            part = ArrayPart.LSquare;
                        } else {
                            AddError(TypeError(token, "identifier"));
                        }
                        break;
                    case ArrayPart.LSquare:
                        if (token.tag == TokenTag.LSquare) {
                            part = ArrayPart.RSquare;
                        } else {
                            AddError(ValueError(token, "<"));
                        }
                        break;
                    case ArrayPart.RSquare:
                        if (token.tag == TokenTag.RSquare) {
                            TypeData baseType = CommonUtil.GetBaseType(baseName);
                            if (baseType != null) return new TypeData(baseName + "[]", baseType);
                            return new TypeData(baseName + "[]", new TypeData(baseName, TypeName.Object));
                        } else {
                            AddError(ValueError(token, "]"));
                        }
                        break;
                }
                index++;
            }
            // 添加解析错误
            if (error.Length == 0) AddArrayPartError(part);
            return null;
        }
        #endregion

        #region String
        private static string CreateGenericName(string baseName, List<TypeData> genericTypes) {
            if (baseName == null) baseName = "";
            StringBuilder sb = new StringBuilder(baseName);
            sb.Append('<');
            for (int i = 0; i < genericTypes.Count; i++) {
                if (i > 0) sb.Append(", ");
                sb.Append(genericTypes[i].ToString());
            }
            sb.Append('>');
            return sb.ToString();
        }
        #endregion

        #region Error
        private void AddError(string error) {
            this.error.Append(error);
        }

        private void AddClassPartError(ClassPart part) {
            switch (part) {
                case ClassPart.ClassType:
                    AddError(LackError(tokens.Last(), "Parse class type error!", "class\" or \"struct"));
                    break;
                case ClassPart.ClassName:
                    AddError(LackError(tokens.Last(), "Parse class name error!", "<identifier>"));
                    break;
                case ClassPart.LBrace:
                    AddError(LackError(tokens.Last(), "Parse class start error!", "{"));
                    break;
                case ClassPart.FieldType or ClassPart.RBrace:
                    AddError(LackError(tokens.Last(), "Parse field type error!", "<identifier>\" or \"}"));
                    break;
                case ClassPart.FieldName:
                    AddError(LackError(tokens.Last(), "Parse field name error!", "<identifier>"));
                    break;
                case ClassPart.Semicolon:
                    AddError(LackError(tokens.Last(), "Parse field end error!", ";"));
                    break;
            }
        }

        private void AddGenericPartError(GenericPart part) {
            switch (part) {
                case GenericPart.BaseType:
                    AddError(LackError(tokens.Last(), "Parse generic base name error!", "<identifier>"));
                    break;
                case GenericPart.LAngle:
                    AddError(LackError(tokens.Last(), "Parse generic start error!", "<"));
                    break;
                case GenericPart.GenericType:
                    AddError(LackError(tokens.Last(), "Parse generic type error!", "<identifier>"));
                    break;
                case GenericPart.Comma or GenericPart.RAngle:
                    AddError(LackError(tokens.Last(), "Parse generic end error!", ">\" or \","));
                    break;
            }
        }

        private void AddArrayPartError(ArrayPart part) {
            switch (part) {
                case ArrayPart.Type:
                    AddError(LackError(tokens.Last(), "Parse array type error!", "<identifier>"));
                    break;
                case ArrayPart.LSquare:
                    AddError(LackError(tokens.Last(), "Parse array start error!", "["));
                    break;
                case ArrayPart.RSquare:
                    AddError(LackError(tokens.Last(), "Parse array end error!", "]"));
                    break;
            }
        }

        /// <summary> 获取错误位置 </summary>
        private static string ErrorPosition(LexicalToken token) {
            return $"Parse token error at ({token.row}, {token.column}), Near \"{token.value}\".";
        }

        /// <summary> 类型错误 </summary>
        private static string TypeError(LexicalToken token, string targetType) {
            return $"{ErrorPosition(token)} \"{token.value}\" is not expected type <{targetType}>";
        }

        /// <summary> 字符串错误 </summary>
        private static string ValueError(LexicalToken token, string targetValue) {
            return $"{ErrorPosition(token)} \"{token.value}\" is not expected value \"{targetValue}\"";
        }

        /// <summary> 缺少错误 </summary>
        private static string LackError(LexicalToken token, string info, string expected) {
            return $"{ErrorPosition(token)} {info} Expected value \"{expected}\"";
        }

        /// <summary> 名称重复错误 </summary>
        private static string FieldNameExistsError(LexicalToken token, string name) {
            return $"{ErrorPosition(token)} Field name : {name} is already exists!";
        }
        #endregion

        #region Clear
        private void ClearData() {
            index = 0;
            tokens = null;
            error = null;
        }
        #endregion

    }

}